package com.googlecode.continuity.plugin.codecomment.client;

import java.util.Iterator;
import java.util.List;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import com.googlecode.continuity.core.client.CoreGWT;
import com.googlecode.continuity.plugin.gwtext.client.ErrorBox;
import com.googlecode.continuity.plugin.gwtext.client.InformationBox;
import com.googlecode.continuity.plugin.gwtext.client.PopupMenu;
import com.googlecode.continuity.plugin.gwtext.client.PromptTextArea;
import com.googlecode.continuity.plugin.svnbrowser.client.SVNBrowser;
import com.googlecode.continuity.plugin.svnbrowser.client.SVNBrowserEventListener;
import com.googlecode.continuity.plugin.svnbrowser.client.SVNRepositoryHandle;
import com.googlecode.continuity.plugin.svnbrowser.client.wrapper.SVNDirEntryWrapper;

/**
 * Comment box for creating a comment. Offers options for severity and visibility, a parent comment chooser
 * and a message window.
 */
class EditCommentBox extends CommentBox {
// ------------------------------ FIELDS ------------------------------

  protected FlexTable relatedCommentList;
  private PromptTextArea message;
  private Label addRelated;

  private int messageRow = 0;
  private int relatedCommentRow = 1;
  private int settingsRow = 2;
  private int controlRow = 3;
  private ListBox severityOptions;

// --------------------------- CONSTRUCTORS ---------------------------

  public EditCommentBox(SVNRepositoryHandle svnHandle, CodeComment comment, CodeCommentArea area) {
    super(svnHandle, comment, area);

    CodePointer location = comment.getLocation();

    // TODO: introduce default
    // set comment severity
    comment.setSeverity((SeverityConstant) SeverityConstant.getInstances().get(0));

    setupMessage();
    setupRelatedComments();
    setupSettings();
    setupControls();
  }

  protected void setupControls() {
    Button saveButton = new Button(messages.saveComment(), new ClickListener() {
      public void onClick(Widget sender) {
        saveComment();
        area.hideCommentBoxFinal();
      }
    });
    content.setWidget(controlRow, 0, saveButton);
    content.getFlexCellFormatter().setHorizontalAlignment(controlRow, 0, HasHorizontalAlignment.ALIGN_RIGHT);
  }

  private void saveComment() {
    comment.setContent(message.getText());
    String selected = severityOptions.getItemText(severityOptions.getSelectedIndex());
    for (int i = 0; i < SeverityConstant.getInstances().size(); i++) {
      if (selected == ((SeverityConstant) SeverityConstant.getInstances().get(i)).getName()) {
        comment.setSeverity((SeverityConstant) SeverityConstant.getInstances().get(i));
        break;
      }
    }

    // TODO: set visibility
    // todo: start loading

    ((CodeCommentRPCAsync) CoreGWT.getRPC("CodeCommentRPCAsync")).addComment(comment, new AsyncCallback() {
      public void onFailure(Throwable throwable) {
        // todo: catch error
      }

      public void onSuccess(Object object) {
        // todo: show message
        // todo: stop loading
        comment = (CodeComment) object;
        area.addComment(comment);
      }
    });
  }

  protected void setupMessage() {
    message = new PromptTextArea(messages.commentMessageBoxPrompt());
    message.addStyleName("messageBox");
    content.setWidget(messageRow, 0, message);
    content.getFlexCellFormatter().addStyleName(messageRow, 0, "messageBoxContainer");
  }

  protected void setupRelatedComments() {
    addRelated = new Label(messages.addRelatedComment());
    final PopupMenu menu = new PopupMenu();
    final SVNBrowser browser = new SVNBrowser(svnHandle);
    browser.setText(messages.chooseFileForComments());

    // position the drop down menu directly under and right aligned with the add parent link
    final PopupPanel.PositionCallback positionDropDown = new PopupPanel.PositionCallback() {
      public void setPosition(int offsetWidth, int offestHeight) {
        menu.setPopupPosition(addRelated.getAbsoluteLeft() + addRelated.getOffsetWidth() - offsetWidth,
            addRelated.getAbsoluteTop() + addRelated.getOffsetHeight() + 2);
      }
    };

    // when a comment has been selected in a file comment list, add it to the parent comments and update parent
    // comment list
    final CodeCommentSelectListener commentSelectListener = new CodeCommentSelectListener() {
      public void onSelect(CodeComment related) {
        browser.hide();
        comment.addRelatedComment(related);
        updateRelatedComments();
      }
    };

    // show a SVN browser that lets you select a file in which a comment can be picked
    final ClickListener otherFileCommentSelector = new ClickListener() {
      public void onClick(Widget widget) {
        browser.addSVNBrowserEventListener(new SVNBrowserEventListener() {
          public void onDirectorySelected(String directoryPath) {
          }

          // on file selection show a list of comments for that file
          public void onFileSelected(SVNDirEntryWrapper entry, String filePath) {
            final CodePointer pointer = new CodePointer();
            pointer.setUrl(entry.getURL());
            pointer.setName(entry.getName());
            pointer.setVersion(entry.getRevision());

            // display a list of comments if available
            final AsyncCallback displayCommentList = new AsyncCallback() {
              public void onFailure(Throwable throwable) {
                // todo: replace with unified error handling
                ErrorBox error = new ErrorBox(throwable);
                error.show();
              }

              public void onSuccess(Object o) {
                List comments = (List) o;
                if (comments.size() > 0) {
                  FileCommentListBox list = new FileCommentListBox(pointer, commentSelectListener);
                  list.addComments(comments);
                  list.show();
                  return;
                }

                InformationBox info = new InformationBox(messages.noPreviousCommentsForFile());
                info.show();
              }
            };

            ((CodeCommentRPCAsync) CoreGWT.getRPC("CodeCommentRPCAsync"))
                .getCommentsForFile(pointer, 0, -1, displayCommentList);
          }
        });
        browser.show();
      }
    };

    // get previous comments for current file to build drop down menu which enables parent comment selection
    ((CodeCommentRPCAsync) CoreGWT.getRPC("CodeCommentRPCAsync"))
        .getCommentsForFile(area.getFileInfo(), 0, -1, new AsyncCallback() {
          public void onFailure(Throwable throwable) {
            // todo: replace with unified error handling
            ErrorBox error = new ErrorBox(throwable);
            error.show();
          }

          public void onSuccess(Object o) {
            final List comments = (List) o;

            if (comments.size() > 0) { // add a line for comments from same file that opens a comment list
              // opens a comment list with retrieved comments
              ClickListener showCommentList = new ClickListener() {
                public void onClick(Widget widget) {
                  FileCommentListBox list = new FileCommentListBox(area.getFileInfo(), commentSelectListener);
                  list.addComments(comments);
                  list.show();
                }
              };

              menu.addLine(messages.fromSameFile(), showCommentList);
            } else { // add an inactive line indicating that no comments are available for this file
              menu.addLine(messages.fromSameFile(), null, null, messages.noPreviousComments(), false);
            }

            // add a line for comments from other files, opening a file browser
            menu.addLine(messages.fromAnotherFile(), otherFileCommentSelector);
          }
        });

    // on click show drop down menu
    addRelated.addClickListener(new ClickListener() {
      public void onClick(Widget widget) {
        // show and position pop up
        menu.setPopupPositionAndShow(positionDropDown);
      }
    });

    addRelated.addStyleName("link");

    updateRelatedComments();
  }

  private void updateRelatedComments() {
    relatedCommentList = new FlexTable();
    relatedCommentList.addStyleName("relatedList");

    /*Label relatedHeader = new Label(messages.relatedCommentsHeader());
    relatedCommentList.setWidget(0, 0, relatedHeader);
    relatedHeader.addStyleName("header");
    relatedCommentList.getFlexCellFormatter().setColSpan(0, 0, 2);
*/
    // todo: prevent row accumulation in IE.
    Iterator it = comment.getRelatedComments().iterator();
    while (it.hasNext()) {
      final CodeComment related = (CodeComment) it.next();
      CodePointer pointer = related.getLocation();
      Label relatedLabel;
      if (pointer.getFirstLine() == pointer.getLastLine()) {
        relatedLabel = new Label(messages.relatedCommentSingleLine(pointer.getName(), pointer.getFirstLine(),
            pointer.getVersion()));
      } else {
        relatedLabel = new Label(messages.relatedCommentMultipleLines(pointer.getName(), pointer.getFirstLine(),
            pointer.getLastLine(), pointer.getVersion()));
      }
//        relatedLabel.addStyleName("link");
      relatedLabel.addStyleName("severity-" + related.getSeverity().getName());
      relatedLabel.addStyleName("relatedListEntry");

      Image delete = new Image(CoreGWT.getWebappRoot() + "img/delete_12.png");
      delete.addStyleName("delete");
      delete.addClickListener(new ClickListener() {
        public void onClick(Widget widget) {
          comment.removeRelatedComment(related);
          updateRelatedComments();
        }
      });

      int row = relatedCommentList.getRowCount();
      relatedCommentList.insertRow(row);
      relatedCommentList.setWidget(row, 0, relatedLabel);
      relatedCommentList.setWidget(row, 1, delete);

      relatedCommentList.getFlexCellFormatter().setHorizontalAlignment(row, 1, HasHorizontalAlignment.ALIGN_RIGHT);
    }

    int row = relatedCommentList.getRowCount();
    relatedCommentList.setWidget(row, 0, addRelated);
    relatedCommentList.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT);
    relatedCommentList.getFlexCellFormatter().setColSpan(row, 0, 2);

    content.setWidget(relatedCommentRow, 0, relatedCommentList);
  }

  protected void setupSettings() {
    // add severity selection
    FlexTable settings = new FlexTable();
    severityOptions = new ListBox();
    for (int i = 0; i < SeverityConstant.getInstances().size(); i++) {
      severityOptions.addItem(((SeverityConstant) SeverityConstant.getInstances().get(i)).getName());
    }

    Label sevLabel = new Label(messages.type());
    settings.setWidget(0, 0, sevLabel);
    settings.setWidget(1, 0, severityOptions);

    // add visibility selection
    ListBox visibilityOptions = new ListBox();
    visibilityOptions.addItem("all");

    Label visLabel = new Label(messages.visibleTo());
    settings.setWidget(0, 1, visLabel);
    settings.setWidget(1, 1, visibilityOptions);

    settings.addStyleName("settings");

    content.setWidget(settingsRow, 0, settings);
  }

// -------------------------- OTHER METHODS --------------------------

  protected boolean isEdited() {
    return !message.showsPrompt() && !message.getText().equals("");
  }
}
